// import com.alibaba.druid.DbType;
// import com.alibaba.druid.sql.ast.*;
// import com.alibaba.druid.sql.ast.SQLKeep.DenseRank;
// import com.alibaba.druid.sql.ast.expr.*;
// import com.alibaba.druid.sql.ast.statement.*;
// import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeIntervalDay;
// import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeIntervalYear;
// import com.alibaba.druid.sql.dialect.oracle.ast.OracleSegmentAttributes;
// import com.alibaba.druid.sql.dialect.oracle.ast.clause->OracleLobStorageClause;
// import com.alibaba.druid.sql.dialect.oracle.ast.clause->OracleStorageClause;
// import com.alibaba.druid.sql.dialect.oracle.ast.clause->OracleStorageClause.OracleStorageClause_FlashCacheType;
// import com.alibaba.druid.sql.dialect.oracle.ast.expr.*;
// import com.alibaba.druid.sql.dialect.oracle.ast.stmt.*;
// import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleConstraint.OracleConstraint_Initially;
// import com.alibaba.druid.sql.parser.*;
// import com.alibaba.druid.util.FnvHash;

// import java.math.BigInteger;
// import java.util.Arrays;
// import java.util.List;

#include "OracleExprParser.h"
#include "../../../../utils/FnvHash.h"
#include "../../../../utils/instanceof.h"
#include "../../../../utils/StringUtils.h"
#include "../../../../utils/log.h"
#include "OracleLexer.h"
#include "../ast/expr/OracleIntervalType.h"
#include "../ast/expr/OracleSysdateExpr.h"
#include "../ast/expr/OracleBinaryFloatExpr.h"
#include "../ast/expr/OracleBinaryDoubleExpr.h"
#include "../ast/expr/OracleCursorExpr.h"
#include "../ast/expr/OracleOuterExpr.h"
#include "../ast/expr/OracleTreatExpr.h"
#include "../ast/expr/OracleRangeExpr.h"
#include "../ast/expr/OracleIntervalExpr.h"
#include "../ast/expr/OracleDatetimeExpr.h"
#include "../ast/expr/OracleAnalytic.h"
#include "../ast/expr/OracleAnalyticWindowing.h"
#include "../ast/expr/OracleIsSetExpr.h"
#include "../ast/expr/OracleIsOfTypeExpr.h"
#include "../ast/stmt/OraclePrimaryKey.h"
#include "../ast/stmt/OracleUsingIndexClause.h"
#include "../ast/stmt/OracleCreateIndexStatement.h"
#include "../ast/stmt/OracleUnique.h"
#include "../ast/stmt/OracleConstraint.h"
#include "../ast/stmt/OracleForeignKey.h"
#include "../ast/stmt/OracleCheck.h"
#include "../ast/clause/OracleLobStorageClause.h"
#include "../ast/clause/OracleStorageClause.h"
#include "../ast/OracleDataTypeIntervalDay.h"
#include "../ast/OracleDataTypeIntervalYear.h"
#include "../../../parser/SQLSelectParser.h"
#include "../../../ast/expr/SQLTimestampExpr.h"
#include "../../../ast/expr/SQLSizeExpr.h"
#include "../../../ast/expr/SQLDbLinkExpr.h"
#include "../../../ast/expr/SQLPropertyExpr.h"
#include "../../../ast/expr/SQLArrayExpr.h"
#include "../../../ast/expr/SQLSequenceExpr.h"
#include "../../../ast/expr/SQLAggregateOption.h"
#include "../../../ast/expr/SQLBinaryOperator.h"
#include "../../../ast/SQLExpr.h"
#include "../../../ast/SQLKeep.h"
#include "../../../ast/SQLPartition.h"
#include "../../../ast/SQLPartitionValue.h"
#include "../../../ast/SQLSubPartition.h"
#include "../../../ast/SQLPartitionByRange.h"
#include "../../../ast/SQLPartitionByRange.h"
#include "../../../ast/SQLPartitionByList.h"
#include "../../../ast/SQLPartitionBy.h"
#include "../../../ast/SQLPartitionByHash.h"
#include "../../../ast/SQLSubPartitionByHash.h"
#include "../../../ast/SQLSubPartitionBy.h"
#include "../../../ast/SQLSubPartitionByList.h"
#include "OracleSelectParser.h"
#include "OracleStatementParser.h"

std::shared_ptr<std::vector<string_ptr>> OracleExprParser::AGGREGATE_FUNCTIONS = std::make_shared<std::vector<string_ptr>>();

std::shared_ptr<std::vector<long>> OracleExprParser::AGGREGATE_FUNCTIONS_CODES = std::make_shared<std::vector<long>>();

BOOL_ptr OracleExprParser::__init = OracleExprParser::init();
BOOL_ptr OracleExprParser::init()
{
  std::shared_ptr<std::vector<string_ptr>> strings = std::make_shared<std::vector<string_ptr>>(std::vector<string_ptr>{
      make_string_ptr("AVG"),
      make_string_ptr("CORR"),
      make_string_ptr("COVAR_POP"),
      make_string_ptr("COVAR_SAMP"),
      make_string_ptr("COUNT"),
      make_string_ptr("CUME_DIST"),
      make_string_ptr("DENSE_RANK"),
      make_string_ptr("FIRST"),
      make_string_ptr("FIRST_VALUE"),
      make_string_ptr("LAG"),
      make_string_ptr("LAST"),
      make_string_ptr("LAST_VALUE"),
      make_string_ptr("LISTAGG"),
      make_string_ptr("LEAD"),
      make_string_ptr("MAX"), //
      make_string_ptr("MIN"),
      make_string_ptr("NTILE"),
      make_string_ptr("PERCENT_RANK"),    //
      make_string_ptr("PERCENTILE_CONT"), //
      make_string_ptr("PERCENTILE_DISC"), //
      make_string_ptr("RANK"),
      make_string_ptr("RATIO_TO_REPORT"),
      make_string_ptr("REGR_SLOPE"),
      make_string_ptr("REGR_INTERCEPT"), //
      make_string_ptr("REGR_COUNT"),     //
      make_string_ptr("REGR_R2"),
      make_string_ptr("REGR_AVGX"), //
      make_string_ptr("REGR_AVGY"), //
      make_string_ptr("REGR_SXX"),  //
      make_string_ptr("REGR_SYY"),  //
      make_string_ptr("REGR_SXY"),
      make_string_ptr("ROW_NUMBER"), //
      make_string_ptr("STDDEV"),     //
      make_string_ptr("STDDEV_POP"), //
      make_string_ptr("STDDEV_SAMP"),
      make_string_ptr("SUM"),
      make_string_ptr("VAR_POP"),
      make_string_ptr("VAR_SAMP"),
      make_string_ptr("VARIANCE"),
      make_string_ptr("WM_CONCAT")});
  AGGREGATE_FUNCTIONS_CODES = FnvHash::fnv1a_64_lower(strings, true);
  // AGGREGATE_FUNCTIONS = new string_ptr[AGGREGATE_FUNCTIONS_CODES->size()];
  for (size_t i = 0; i < AGGREGATE_FUNCTIONS_CODES->size(); i++)
  {
    AGGREGATE_FUNCTIONS->push_back(make_string_ptr(""));
  }
  for (string_ptr str : *strings)
  {
    long hash = FnvHash::fnv1a_64_lower(str);
    // int index = Arrays.binarySearch(AGGREGATE_FUNCTIONS_CODES, hash);
    // AGGREGATE_FUNCTIONS[index] = str;
    int index = -1;
    int size = strings->size();
    for (int i = 0; i < size; i++)
    {
      // LOG_INFO << __FUNCTION__ << ": i:" << i << ",size:" << size;
      if (OracleExprParser::AGGREGATE_FUNCTIONS_CODES->at(i) == hash)
      {
        index = i;
        break;
      }
    }
    // LOG_INFO << "index:" << index;
    AGGREGATE_FUNCTIONS->at(index) = str;
  }
}

OracleExprParser::OracleExprParser(Lexer_ptr lexer)
    : SQLExprParser(lexer)
{
  this->aggregateFunctions = AGGREGATE_FUNCTIONS;
  this->aggregateFunctionHashCodes = AGGREGATE_FUNCTIONS_CODES;
  this->dbType = DbType_ptr(new DbType(&DbType::oracle));
}

OracleExprParser::OracleExprParser(string_ptr text)
    : OracleExprParser(OracleLexer_ptr(new OracleLexer(text)))
{

  this->lexer->nextToken();
  this->dbType = DbType_ptr(new DbType(&DbType::oracle));
}

OracleExprParser::OracleExprParser(string_ptr text, SQLParserFeature_list_ptr features)
    : OracleExprParser(OracleLexer_ptr(new OracleLexer(text, features)))
{
  this->lexer->nextToken();
  this->dbType = DbType_ptr(new DbType(&DbType::oracle));
}

BOOL_ptr OracleExprParser::isCharType(long hash)
{
  if (hash == FnvHash::Constants::CHAR ||
      hash == FnvHash::Constants::CHARACTER ||
      hash == FnvHash::Constants::NCHAR ||
      hash == FnvHash::Constants::VARCHAR ||
      hash == FnvHash::Constants::VARCHAR2 ||
      hash == FnvHash::Constants::NVARCHAR ||
      hash == FnvHash::Constants::NVARCHAR2 ||
      hash == FnvHash::Constants::CHARACTER)
  {
    return BOOL::TRUE;
  }
  else
  {
    return BOOL::FALSE;
  }
}

SQLDataType_ptr OracleExprParser::parseDataType(BOOL_ptr restrict)
{
  if (lexer->token()->name->c_str() == Token::CONSTRAINT.name->c_str() ||
      lexer->token()->name->c_str() == Token::COMMA.name->c_str())
  {
    return nullptr;
  }

  if (lexer->token()->name->c_str() == Token::DEFAULT.name->c_str() ||
      lexer->token()->name->c_str() == Token::NOT.name->c_str() ||
      lexer->token()->name->c_str() == Token::XNULL.name->c_str())
  {
    return nullptr;
  }

  if (lexer->token()->name->c_str() == Token::INTERVAL.name->c_str())
  {
    lexer->nextToken();
    if (lexer->identifierEquals(make_string_ptr("YEAR")))
    {
      lexer->nextToken();
      OracleDataTypeIntervalYear_ptr interval = OracleDataTypeIntervalYear_ptr(new OracleDataTypeIntervalYear());

      if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
      {
        lexer->nextToken();
        interval->addArgument(this->expr());
        accept(Token::RPAREN);
      }

      accept(Token::TO);
      acceptIdentifier("MONTH");

      return interval;
    }
    else
    {
      acceptIdentifier("DAY");
      OracleDataTypeIntervalDay_ptr interval = OracleDataTypeIntervalDay_ptr(new OracleDataTypeIntervalDay());
      if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
      {
        lexer->nextToken();
        interval->addArgument(this->expr());
        accept(Token::RPAREN);
      }

      accept(Token::TO);
      acceptIdentifier("SECOND");

      if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
      {
        lexer->nextToken();
        interval->getFractionalSeconds()->push_back(this->expr());
        accept(Token::RPAREN);
      }

      return interval;
    }
  }

  string_ptr typeName;
  if (lexer->token()->name->c_str() == Token::EXCEPTION.name->c_str())
  {
    typeName = make_string_ptr("EXCEPTION");
    lexer->nextToken();
  }
  else if (lexer->identifierEquals(FnvHash::Constants::LONG))
  {
    lexer->nextToken();

    if (lexer->identifierEquals(FnvHash::Constants::RAW))
    {
      lexer->nextToken();
      typeName = make_string_ptr("LONG RAW");
    }
    else
    {
      typeName = make_string_ptr("LONG");
    }
  }
  else
  {
    SQLName_ptr typeExpr = name();
    typeName = typeExpr->toString();
  }

  if ("TIMESTAMP" == typeName->c_str())
  {
    SQLDataTypeImpl_ptr timestamp = SQLDataTypeImpl_ptr(new SQLDataTypeImpl(typeName));
    timestamp->setDbType(dbType);

    if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      lexer->nextToken();
      timestamp->addArgument(this->expr());
      accept(Token::RPAREN);
    }

    if (lexer->token()->name->c_str() == Token::WITH.name->c_str())
    {
      lexer->nextToken();

      if (lexer->identifierEquals(make_string_ptr("LOCAL")))
      {
        lexer->nextToken();
        timestamp->setWithLocalTimeZone(BOOL::TRUE);
      }

      timestamp->setWithTimeZone(BOOL::TRUE);

      acceptIdentifier("TIME");
      acceptIdentifier("ZONE");
    }

    return timestamp;
  }

  if ("national" == typeName->c_str() && isCharType(lexer->hashLCase()))
  {
    typeName->append(" ");
    typeName->append(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    lexer->nextToken();

    if (lexer->identifierEquals(make_string_ptr("VARYING")))
    {
      typeName->append(" ");
      typeName->append(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      lexer->nextToken();
    }

    SQLCharacterDataType_ptr charType = SQLCharacterDataType_ptr(new SQLCharacterDataType(typeName));

    if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      lexer->nextToken();
      SQLExpr_ptr arg = this->expr();
      arg->setParent(charType);
      charType->addArgument(arg);
      accept(Token::RPAREN);
    }

    charType = std::dynamic_pointer_cast<SQLCharacterDataType>(parseCharTypeRest(charType));

    if (lexer->token()->name->c_str() == Token::HINT.name->c_str())
    {
      SQLCommentHint_list_ptr hints = this->parseHints();
      charType->setHints(hints);
    }

    return charType;
  }

  if (SQLExprParser::isCharType(typeName))
  {
    if (lexer->identifierEquals(make_string_ptr("VARYING")))
    {
      typeName->append(" ");
      typeName->append(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      lexer->nextToken();
    }

    SQLCharacterDataType_ptr charType = SQLCharacterDataType_ptr(new SQLCharacterDataType(typeName));

    if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      lexer->nextToken();

      charType->addArgument(this->expr());

      if (lexer->identifierEquals(make_string_ptr("CHAR")))
      {
        lexer->nextToken();
        charType->setCharType(SQLCharacterDataType::CHAR_TYPE_CHAR);
      }
      else if (lexer->identifierEquals(make_string_ptr("BYTE")))
      {
        lexer->nextToken();
        charType->setCharType(SQLCharacterDataType::CHAR_TYPE_BYTE);
      }

      accept(Token::RPAREN);
    }
    else if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
    {
      return parseCharTypeRest(charType);
    }
    else if (restrict)
    {
      accept(Token::LPAREN);
    }

    return parseCharTypeRest(charType);
  }

  if (lexer->token()->name->c_str() == Token::PERCENT.name->c_str())
  {
    lexer->nextToken();
    if (lexer->identifierEquals(make_string_ptr("TYPE")))
    {
      lexer->nextToken();
      typeName->append("%TYPE");
    }
    else if (lexer->identifierEquals(make_string_ptr("ROWTYPE")))
    {
      lexer->nextToken();
      typeName->append("%ROWTYPE");
    }
    else
    {
      std::string tmp = "syntax error : ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }
  }

  SQLDataTypeImpl_ptr dataType = SQLDataTypeImpl_ptr(new SQLDataTypeImpl(typeName));
  dataType->setDbType(dbType);
  return parseDataTypeRest(dataType);
}

SQLExpr_ptr OracleExprParser::primary()
{
  Token_ptr tok = lexer->token();

  SQLExpr_ptr sqlExpr = nullptr;
  if (tok->name->c_str() == Token::SYSDATE.name->c_str())
  {
    lexer->nextToken();
    OracleSysdateExpr_ptr sysdate = OracleSysdateExpr_ptr(new OracleSysdateExpr());
    if (lexer->token()->name->c_str() == Token::MONKEYS_AT.name->c_str())
    {
      lexer->nextToken();
      accept(Token::BANG);
      sysdate->setOption(make_string_ptr("!"));
    }
    sqlExpr = sysdate;
    return primaryRest(sqlExpr);
  }
  else if (tok->name->c_str() == Token::PRIOR.name->c_str())
  {
    lexer->nextToken();
    sqlExpr = expr();
    sqlExpr = SQLUnaryExpr_ptr(new SQLUnaryExpr(SQLUnaryOperator_ptr(new SQLUnaryOperator(&SQLUnaryOperator::Prior)), sqlExpr));
    return primaryRest(sqlExpr);
  }
  else if (tok->name->c_str() == Token::COLON.name->c_str())
  {
    lexer->nextToken();
    if (lexer->token()->name->c_str() == Token::LITERAL_INT.name->c_str())
    {
      std::string tmp = ":";
      tmp += encode_util::UnicodeToUTF8(lexer->numberString());
      string_ptr name = make_string_ptr(tmp);
      lexer->nextToken();
      return SQLVariantRefExpr_ptr(new SQLVariantRefExpr(name));
    }
    else if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str())
    {
      string_ptr name = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      if (name->at(0) == 'B' || name->at(0) == 'b')
      {
        lexer->nextToken();
        std::string tmp = ":";
        tmp += name->c_str();
        return SQLVariantRefExpr_ptr(new SQLVariantRefExpr(make_string_ptr(tmp)));
      }
      std::string tmp = "syntax error : ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }
    else
    {
      std::string tmp = "syntax error : ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }
  }
  else if (tok->name->c_str() == Token::LITERAL_ALIAS.name->c_str())
  {
    string_ptr alias = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    lexer->nextToken();
    return primaryRest(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(alias)));
  }
  else if (tok->name->c_str() == Token::BINARY_FLOAT.name->c_str())
  {
    OracleBinaryFloatExpr_ptr floatExpr = OracleBinaryFloatExpr_ptr(new OracleBinaryFloatExpr());
    floatExpr->setValue(atof(encode_util::UnicodeToUTF8(lexer->numberString()).c_str()));
    lexer->nextToken();
    return primaryRest(floatExpr);
  }
  else if (tok->name->c_str() == Token::BINARY_DOUBLE.name->c_str())
  {
    OracleBinaryDoubleExpr_ptr doubleExpr = OracleBinaryDoubleExpr_ptr(new OracleBinaryDoubleExpr());
    doubleExpr->setValue(std::stod(encode_util::UnicodeToUTF8(lexer->numberString()).c_str()));

    lexer->nextToken();
    return primaryRest(doubleExpr);
  }
  else if (tok->name->c_str() == Token::TABLE.name->c_str())
  {
    lexer->nextToken();
    return primaryRest(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(make_string_ptr("TABLE"))));
  }
  else if (tok->name->c_str() == Token::PLUS.name->c_str())
  {
    lexer->nextToken();
    if (lexer->token()->name->c_str() == Token::LITERAL_INT.name->c_str())
    {
      sqlExpr = SQLIntegerExpr_ptr(new SQLIntegerExpr(lexer->integerValue()));
      lexer->nextToken();
    }
    else if (lexer->token()->name->c_str() == Token::LITERAL_FLOAT.name->c_str())
    {
      sqlExpr = lexer->numberExpr();
      lexer->nextToken();
    }
    else if (lexer->token()->name->c_str() == Token::BINARY_FLOAT.name->c_str())
    {
      sqlExpr = OracleBinaryFloatExpr_ptr(new OracleBinaryFloatExpr(atof(encode_util::UnicodeToUTF8(lexer->numberString()).c_str())));
      lexer->nextToken();
    }
    else if (lexer->token()->name->c_str() == Token::BINARY_DOUBLE.name->c_str())
    {
      sqlExpr = OracleBinaryDoubleExpr_ptr(new OracleBinaryDoubleExpr(std::stod(encode_util::UnicodeToUTF8(lexer->numberString()).c_str())));
      lexer->nextToken();
    }
    else if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      lexer->nextToken();
      sqlExpr = expr();
      accept(Token::RPAREN);
      sqlExpr = SQLUnaryExpr_ptr(new SQLUnaryExpr(SQLUnaryOperator_ptr(new SQLUnaryOperator(&SQLUnaryOperator::Plus)), sqlExpr));
    }
    else if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str())
    {
      sqlExpr = expr();
      sqlExpr = SQLUnaryExpr_ptr(new SQLUnaryExpr(SQLUnaryOperator_ptr(new SQLUnaryOperator(&SQLUnaryOperator::Plus)), sqlExpr));
    }
    else
    {
      std::string tmp = "TODO ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }
    return primaryRest(sqlExpr);
  }
  else if (tok->name->c_str() == Token::SUB.name->c_str())
  {
    lexer->nextToken();

    if (lexer->token()->name->c_str() == Token::LITERAL_INT.name->c_str())
    {
      long integerValue = lexer->integerValue();
      // if (integerValue instanceof Integer) {
      //     int intVal = ((Integer) integerValue).intValue();
      //     if (intVal == Integer.MIN_VALUE) {
      //         integerValue = Long.valueOf(((long) intVal) * -1);
      //     } else {
      //         integerValue = Integer.valueOf(intVal * -1);
      //     }
      // } else if (integerValue instanceof Long) {
      long longVal = integerValue;
      // if (longVal == 2147483648L) {
      // integerValue = Integer.valueOf((int) (((long) longVal) * -1));
      integerValue = longVal * -1;
      // } else {
      //     integerValue = Long.valueOf(longVal * -1);
      // }
      // } else {
      //     integerValue = ((BigInteger) integerValue).negate();
      // }
      sqlExpr = SQLIntegerExpr_ptr(new SQLIntegerExpr(integerValue));
      lexer->nextToken();
    }
    else if (lexer->token()->name->c_str() == Token::LITERAL_FLOAT.name->c_str())
    {
      sqlExpr = lexer->numberExpr(BOOL::TRUE);
      lexer->nextToken();
    }
    else if (lexer->token()->name->c_str() == Token::BINARY_FLOAT.name->c_str())
    {
      sqlExpr = OracleBinaryFloatExpr_ptr(new OracleBinaryFloatExpr(atof(encode_util::UnicodeToUTF8(lexer->numberString()).c_str()) * -1));
      lexer->nextToken();
    }
    else if (lexer->token()->name->c_str() == Token::BINARY_DOUBLE.name->c_str())
    {
      sqlExpr = OracleBinaryDoubleExpr_ptr(new OracleBinaryDoubleExpr(std::stod(encode_util::UnicodeToUTF8(lexer->numberString()).c_str()) * -1));
      lexer->nextToken();
    }
    else if (lexer->token()->name->c_str() == Token::VARIANT.name->c_str() ||
             lexer->token()->name->c_str() == Token::QUES.name->c_str() ||
             lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str() ||
             lexer->token()->name->c_str() == Token::LITERAL_ALIAS.name->c_str())
    {
      sqlExpr = expr();
      sqlExpr = SQLUnaryExpr_ptr(new SQLUnaryExpr(SQLUnaryOperator_ptr(new SQLUnaryOperator(&SQLUnaryOperator::Negative)), sqlExpr));
    }
    else if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      lexer->nextToken();
      sqlExpr = expr();
      accept(Token::RPAREN);
      sqlExpr = SQLUnaryExpr_ptr(new SQLUnaryExpr(SQLUnaryOperator_ptr(new SQLUnaryOperator(&SQLUnaryOperator::Negative)), sqlExpr));
    }
    else
    {
      std::string tmp = "TODO ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }
    return primaryRest(sqlExpr);
  }

  else if (tok->name->c_str() == Token::CURSOR.name->c_str())
  {
    lexer->nextToken();
    accept(Token::LPAREN);

    SQLSelect_ptr select = createSelectParser()->select();
    OracleCursorExpr_ptr cursorExpr = OracleCursorExpr_ptr(new OracleCursorExpr(select));

    accept(Token::RPAREN);

    sqlExpr = cursorExpr;
    return primaryRest(sqlExpr);
  }
  //           case MODEL:
  else if (tok->name->c_str() == Token::PCTFREE.name->c_str() ||
           tok->name->c_str() == Token::INITRANS.name->c_str() ||
           tok->name->c_str() == Token::MAXTRANS.name->c_str() ||
           tok->name->c_str() == Token::SEGMENT.name->c_str() ||
           tok->name->c_str() == Token::CREATION.name->c_str() ||
           tok->name->c_str() == Token::IMMEDIATE.name->c_str() ||
           tok->name->c_str() == Token::DEFERRED.name->c_str() ||
           tok->name->c_str() == Token::STORAGE.name->c_str() ||
           tok->name->c_str() == Token::NEXT.name->c_str() ||
           tok->name->c_str() == Token::MINEXTENTS.name->c_str() ||
           tok->name->c_str() == Token::MAXEXTENTS.name->c_str() ||
           tok->name->c_str() == Token::MAXSIZE.name->c_str() ||
           tok->name->c_str() == Token::PCTINCREASE.name->c_str() ||
           tok->name->c_str() == Token::FLASH_CACHE.name->c_str() ||
           tok->name->c_str() == Token::CELL_FLASH_CACHE.name->c_str() ||
           tok->name->c_str() == Token::NONE.name->c_str() ||
           tok->name->c_str() == Token::LOB.name->c_str() ||
           tok->name->c_str() == Token::STORE.name->c_str() ||
           tok->name->c_str() == Token::ROW.name->c_str() ||
           tok->name->c_str() == Token::CHUNK.name->c_str() ||
           tok->name->c_str() == Token::CACHE.name->c_str() ||
           tok->name->c_str() == Token::NOCACHE.name->c_str() ||
           tok->name->c_str() == Token::LOGGING.name->c_str() ||
           tok->name->c_str() == Token::NOCOMPRESS.name->c_str() ||
           tok->name->c_str() == Token::KEEP_DUPLICATES.name->c_str() ||
           tok->name->c_str() == Token::EXCEPTIONS.name->c_str() ||
           tok->name->c_str() == Token::PURGE.name->c_str() ||
           tok->name->c_str() == Token::OUTER.name->c_str())
  {
    sqlExpr = SQLIdentifierExpr_ptr(new SQLIdentifierExpr(make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()))));
    lexer->nextToken();
    return primaryRest(sqlExpr);
  }
  else
  {
    return SQLExprParser::primary();
  }
}

// @Override
SQLExpr_ptr OracleExprParser::methodRest(SQLExpr_ptr expr, BOOL_ptr acceptLPAREN)
{
  if (acceptLPAREN)
  {
    accept(Token::LPAREN);
  }

  if (lexer->token()->name->c_str() == Token::PLUS.name->c_str())
  {
    lexer->nextToken();
    accept(Token::RPAREN);
    return OracleOuterExpr_ptr(new OracleOuterExpr(expr));
  }

  if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
  {
    string_ptr methodName = (std::dynamic_pointer_cast<SQLIdentifierExpr>(expr))->getName();
    SQLMethodInvokeExpr_ptr methodExpr = SQLMethodInvokeExpr_ptr(new SQLMethodInvokeExpr(methodName));
    if ("treat" == methodName->c_str())
    {
      OracleTreatExpr_ptr treatExpr = OracleTreatExpr_ptr(new OracleTreatExpr());

      treatExpr->setExpr(this->expr());

      accept(Token::AS);

      if (lexer->identifierEquals(make_string_ptr("REF")))
      {
        treatExpr->setRef(BOOL::TRUE);
        lexer->nextToken();
      }

      treatExpr->setType(this->expr());
      accept(Token::RPAREN);

      return primaryRest(treatExpr);
    }
  }

  return SQLExprParser::methodRest(expr, BOOL::FALSE);
}

SQLExpr_ptr OracleExprParser::primaryRest(SQLExpr_ptr expr)
{
  if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
  {
    SQLIdentifierExpr_ptr identExpr = std::dynamic_pointer_cast<SQLIdentifierExpr>(expr);
    string_ptr ident = (std::dynamic_pointer_cast<SQLIdentifierExpr>(expr))->getName();

    long hashCode64 = identExpr->hashCode64();
    if (hashCode64 == FnvHash::Constants::TIMESTAMP)
    {
      if (lexer->token()->name->c_str() != Token::LITERAL_ALIAS.name->c_str() &&
          lexer->token()->name->c_str() != Token::LITERAL_CHARS.name->c_str())
      {
        return SQLIdentifierExpr_ptr(new SQLIdentifierExpr(make_string_ptr("TIMESTAMP")));
      }

      SQLTimestampExpr_ptr timestamp = SQLTimestampExpr_ptr(new SQLTimestampExpr());

      string_ptr literal = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      timestamp->setLiteral(literal);
      accept(Token::LITERAL_CHARS);

      if (lexer->identifierEquals(make_string_ptr("AT")))
      {
        lexer->nextToken();
        acceptIdentifier("TIME");
        acceptIdentifier("ZONE");

        string_ptr timezone = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        timestamp->setTimeZone(timezone);
        accept(Token::LITERAL_CHARS);
      }

      return primaryRest(timestamp);
    }
  }
  if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str() &&
      instanceof <SQLExpr, SQLNumericLiteralExpr>(expr))
  {
    string_ptr ident = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));

    if (ident->length() == 1)
    {
      char unit = ident->at(0);
      switch (unit)
      {
      case 'K':
      case 'M':
      case 'G':
      case 'T':
      case 'P':
      case 'E':
      case 'k':
      case 'm':
      case 'g':
      case 't':
      case 'p':
      case 'e':
        expr = SQLSizeExpr_ptr(new SQLSizeExpr(expr, SQLSizeExpr_Unit::valueOf(StringUtils::toUpper(ident))));
        lexer->nextToken();
        break;
      default:
        break;
      }
    }
  }

  if (lexer->token()->name->c_str() == Token::DOTDOT.name->c_str())
  {
    lexer->nextToken();
    SQLExpr_ptr upBound = getExpr();

    return OracleRangeExpr_ptr(new OracleRangeExpr(expr, upBound));
  }

  if (lexer->token()->name->c_str() == Token::MONKEYS_AT.name->c_str())
  {
    lexer->nextToken();

    SQLDbLinkExpr_ptr dblink = SQLDbLinkExpr_ptr(new SQLDbLinkExpr());
    dblink->setExpr(expr);

    if (lexer->token()->name->c_str() == Token::BANG.name->c_str())
    {
      dblink->setDbLink(make_string_ptr("!"));
      lexer->nextToken();
    }
    else
    {
      string_ptr link = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      if (lexer->token()->name->c_str() == Token::LITERAL_ALIAS.name->c_str())
      {
        accept(Token::LITERAL_ALIAS);
      }
      else
      {
        accept(Token::IDENTIFIER);
      }
      dblink->setDbLink(link);
      if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
      {
        SQLExpr_ptr dblinkExpr = dblink->getExpr();
        if (dblinkExpr->getClass()->c_str() == SQLPropertyExpr::class_->c_str())
        {
          SQLExpr_ptr methodInvokeExpr = SQLMethodInvokeExpr_ptr(new SQLMethodInvokeExpr());
          SQLMethodInvokeExpr_ptr expr2 = std::dynamic_pointer_cast<SQLMethodInvokeExpr>(methodRest(methodInvokeExpr, BOOL::TRUE));

          expr2->setOwner((std::dynamic_pointer_cast<SQLPropertyExpr>(expr))->getOwner());
          expr2->setMethodName((std::dynamic_pointer_cast<SQLPropertyExpr>(expr))->getName());

          dblink->setExpr(expr2);
        }
      }
    }

    expr = dblink;
  }

  if (lexer->token()->name->c_str() == Token::LBRACKET.name->c_str())
  {
    SQLArrayExpr_ptr arrayExpr = SQLArrayExpr_ptr(new SQLArrayExpr());
    arrayExpr->setExpr(expr);
    lexer->nextToken();
    this->exprList(arrayExpr->getValues(), arrayExpr);
    accept(Token::RBRACKET);
    expr = arrayExpr;

    expr = primaryRest(expr);
  }

  if (lexer->identifierEquals(make_string_ptr("DAY")) ||
      lexer->identifierEquals(make_string_ptr("YEAR")))
  {
    Lexer_SavePoint_ptr savePoint = lexer->mark();

    string_ptr name = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    lexer->nextToken();

    if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
    {
      lexer->reset(savePoint);
      return expr;
    }

    OracleIntervalExpr_ptr interval = OracleIntervalExpr_ptr(new OracleIntervalExpr());
    interval->setValue(expr);
    OracleIntervalType_ptr type = OracleIntervalType::valueOf(StringUtils::toUpper(name));
    interval->setType(type);

    if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      lexer->nextToken();
      if (lexer->token()->name->c_str() != Token::LITERAL_INT.name->c_str())
      {
        std::string tmp = "syntax error : ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }
      interval->setPrecision(lexer->integerValue());
      lexer->nextToken();
      accept(Token::RPAREN);
    }

    if (lexer->token()->name->c_str() == Token::TO.name->c_str())
    {
      lexer->nextToken();
    }
    else
    {
      lexer->reset(savePoint);
      return expr;
    }

    if (lexer->identifierEquals(make_string_ptr("SECOND")))
    {
      lexer->nextToken();
      interval->setToType(OracleIntervalType::SECOND);
      if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
      {
        lexer->nextToken();
        if (lexer->token()->name->c_str() != Token::LITERAL_INT.name->c_str())
        {
          std::string tmp = "syntax error. ";
          tmp += encode_util::UnicodeToUTF8(lexer->info());
          throw new ParserException(make_string_ptr(tmp));
        }
        interval->setFactionalSecondsPrecision(lexer->integerValue());
        lexer->nextToken();
        accept(Token::RPAREN);
      }
    }
    else
    {
      interval->setToType(OracleIntervalType::MONTH);
      lexer->nextToken();
    }

    expr = interval;
  }

  if (lexer->identifierEquals(make_string_ptr("AT")))
  {
    Lexer_SavePoint_ptr mark = lexer->mark();
    lexer->nextToken();
    if (lexer->identifierEquals(make_string_ptr("LOCAL")))
    {
      lexer->nextToken();
      expr = OracleDatetimeExpr_ptr(new OracleDatetimeExpr(expr, SQLIdentifierExpr_ptr(new SQLIdentifierExpr(make_string_ptr("LOCAL")))));
    }
    else
    {
      if (lexer->identifierEquals(make_string_ptr("TIME")))
      {
        lexer->nextToken();
      }
      else
      {
        lexer->reset(mark);
        return expr;
      }
      acceptIdentifier("ZONE");

      SQLExpr_ptr timeZone = primary();
      expr = OracleDatetimeExpr_ptr(new OracleDatetimeExpr(expr, timeZone));
    }
  }

  SQLExpr_ptr restExpr = SQLExprParser::primaryRest(expr);

  if (restExpr != expr && instanceof <SQLExpr, SQLMethodInvokeExpr>(restExpr))
  {
    SQLMethodInvokeExpr_ptr methodInvoke = std::dynamic_pointer_cast<SQLMethodInvokeExpr>(restExpr);
    if (methodInvoke->getArguments()->size() == 1)
    {
      SQLExpr_ptr paramExpr = *(methodInvoke->getArguments()->begin());
      if (instanceof <SQLExpr, SQLIdentifierExpr>(paramExpr) &&
                         "+" == ((std::dynamic_pointer_cast<SQLIdentifierExpr>(paramExpr))->getName()->c_str()))
      {
        OracleOuterExpr_ptr outerExpr = OracleOuterExpr_ptr(new OracleOuterExpr());
        if (methodInvoke->getOwner() == nullptr)
        {
          outerExpr->setExpr(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(methodInvoke->getMethodName())));
        }
        else
        {
          outerExpr->setExpr(SQLPropertyExpr_ptr(new SQLPropertyExpr(methodInvoke->getOwner(), methodInvoke->getMethodName())));
        }
        return outerExpr;
      }
    }
  }

  return restExpr;
}

SQLExpr_ptr OracleExprParser::dotRest(SQLExpr_ptr expr)
{
  if (lexer->token()->name->c_str() == Token::LITERAL_ALIAS.name->c_str())
  {
    string_ptr name = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    lexer->nextToken();
    expr = SQLPropertyExpr_ptr(new SQLPropertyExpr(expr, name));

    if (lexer->token()->name->c_str() == Token::DOT.name->c_str())
    {
      lexer->nextToken();
      expr = dotRest(expr);
    }

    return expr;
  }

  if (lexer->identifierEquals(FnvHash::Constants::NEXTVAL))
  {
    if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
    {
      SQLIdentifierExpr_ptr identExpr = std::dynamic_pointer_cast<SQLIdentifierExpr>(expr);
      SQLSequenceExpr_ptr seqExpr = SQLSequenceExpr_ptr(new SQLSequenceExpr(identExpr, SQLSequenceExpr_Function_ptr(new SQLSequenceExpr_Function(&SQLSequenceExpr_Function::NextVal))));
      lexer->nextToken();
      return seqExpr;
    }
  }
  else if (lexer->identifierEquals(FnvHash::Constants::CURRVAL))
  {
    if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
    {
      SQLIdentifierExpr_ptr identExpr = std::dynamic_pointer_cast<SQLIdentifierExpr>(expr);
      SQLSequenceExpr_ptr seqExpr = SQLSequenceExpr_ptr(new SQLSequenceExpr(identExpr, SQLSequenceExpr_Function_ptr(new SQLSequenceExpr_Function(&SQLSequenceExpr_Function::CurrVal))));
      lexer->nextToken();
      return seqExpr;
    }
  }

  return SQLExprParser::dotRest(expr);
}

SQLAggregateExpr_ptr OracleExprParser::parseAggregateExpr(string_ptr methodName)
{
  //        methodName = methodName.toUpperCase();

  SQLAggregateExpr_ptr aggregateExpr;
  if (lexer->token()->name->c_str() == Token::UNIQUE.name->c_str())
  {
    aggregateExpr = SQLAggregateExpr_ptr(new SQLAggregateExpr(methodName, SQLAggregateOption::UNIQUE));
    lexer->nextToken();
  }
  else if (lexer->token()->name->c_str() == (Token::ALL.name->c_str()))
  {
    aggregateExpr = SQLAggregateExpr_ptr(new SQLAggregateExpr(methodName, SQLAggregateOption::ALL));
    lexer->nextToken();
  }
  else if (lexer->token()->name->c_str() == (Token::DISTINCT.name->c_str()))
  {
    aggregateExpr = SQLAggregateExpr_ptr(new SQLAggregateExpr(methodName, SQLAggregateOption::DISTINCT));
    lexer->nextToken();
  }
  else
  {
    aggregateExpr = SQLAggregateExpr_ptr(new SQLAggregateExpr(methodName));
  }
  exprList(aggregateExpr->getArguments(), aggregateExpr);

  if (encode_util::UnicodeToUTF8(*lexer->stringVal()) == "IGNORE")
  {
    lexer->nextToken();
    acceptIdentifier("NULLS");
    aggregateExpr->setIgnoreNulls(BOOL::TRUE);
  }
  else if (lexer->identifierEquals(FnvHash::Constants::RESPECT))
  {
    lexer->nextToken();
    acceptIdentifier("NULLS");
    aggregateExpr->setIgnoreNulls(BOOL::FALSE);
  }

  accept(Token::RPAREN);

  if (encode_util::UnicodeToUTF8(*lexer->stringVal()) == ("IGNORE"))
  {
    lexer->nextToken();
    acceptIdentifier("NULLS");
    aggregateExpr->setIgnoreNulls(BOOL::TRUE);
  }
  else if (lexer->identifierEquals(FnvHash::Constants::RESPECT))
  {
    lexer->nextToken();
    acceptIdentifier("NULLS");
    aggregateExpr->setIgnoreNulls(BOOL::FALSE);
  }

  if (lexer->identifierEquals(FnvHash::Constants::WITHIN))
  {
    lexer->nextToken();
    accept(Token::GROUP);
    accept(Token::LPAREN);
    SQLOrderBy_ptr orderBy = this->parseOrderBy();
    aggregateExpr->setWithinGroup(BOOL::TRUE);
    aggregateExpr->setOrderBy(orderBy);
    accept(Token::RPAREN);
  }

  if (lexer->identifierEquals(make_string_ptr("KEEP")))
  {
    lexer->nextToken();

    SQLKeep_ptr keep = SQLKeep_ptr(new SQLKeep());
    accept(Token::LPAREN);
    acceptIdentifier("DENSE_RANK");
    if (lexer->identifierEquals(make_string_ptr("FIRST")))
    {
      lexer->nextToken();
      keep->setDenseRank(SQLKeep_DenseRank::FIRST);
    }
    else
    {
      acceptIdentifier("LAST");
      keep->setDenseRank(SQLKeep_DenseRank::LAST);
    }

    SQLOrderBy_ptr orderBy = this->parseOrderBy();
    keep->setOrderBy(orderBy);

    aggregateExpr->setKeep(keep);

    accept(Token::RPAREN);
  }

  if (lexer->token()->name->c_str() == Token::OVER.name->c_str())
  {
    OracleAnalytic_ptr over = OracleAnalytic_ptr(new OracleAnalytic());

    lexer->nextToken();
    accept(Token::LPAREN);

    if (lexer->token()->name->c_str() == Token::PARTITION.name->c_str())
    {
      lexer->nextToken();
      accept(Token::BY);

      if (lexer->token()->name->c_str() == (Token::LPAREN.name->c_str()))
      {
        lexer->nextToken();
        exprList(over->getPartitionBy(), over);
        accept(Token::RPAREN);
      }
      else
      {
        exprList(over->getPartitionBy(), over);
      }
    }

    SQLOrderBy_ptr orderBy = parseOrderBy();
    if (orderBy != nullptr)
    {
      over->setOrderBy(orderBy);

      OracleAnalyticWindowing_ptr windowing = nullptr;
      if (lexer->identifierEquals(FnvHash::Constants::ROWS))
      {
        lexer->nextToken();
        windowing = OracleAnalyticWindowing_ptr(new OracleAnalyticWindowing());
        windowing->setType(OracleAnalyticWindowing_Type::ROWS);
      }
      else if (lexer->identifierEquals(FnvHash::Constants::RANGE))
      {
        lexer->nextToken();
        windowing = OracleAnalyticWindowing_ptr(new OracleAnalyticWindowing());
        windowing->setType(OracleAnalyticWindowing_Type::RANGE);
      }

      if (windowing != nullptr)
      {
        if (lexer->identifierEquals(FnvHash::Constants::CURRENT))
        {
          lexer->nextToken();
          accept(Token::ROW);
          windowing->setExpr(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(make_string_ptr("CURRENT ROW"))));
          over->setWindowing(windowing);
        }
        if (lexer->identifierEquals(FnvHash::Constants::UNBOUNDED))
        {
          lexer->nextToken();
          if (encode_util::UnicodeToUTF8(*lexer->stringVal()) == ("PRECEDING"))
          {
            lexer->nextToken();
            windowing->setExpr(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(make_string_ptr("UNBOUNDED PRECEDING"))));
          }
          else
          {
            std::string tmp = "syntax error. ";
            tmp += encode_util::UnicodeToUTF8(lexer->info());
            throw new ParserException(make_string_ptr(tmp));
          }
        }
        else if (lexer->token()->name->c_str() == Token::BETWEEN.name->c_str())
        {
          lexer->nextToken();
          SQLExpr_ptr beginExpr;

          if (lexer->identifierEquals(FnvHash::Constants::CURRENT))
          {
            lexer->nextToken();
            accept(Token::ROW);
            beginExpr = SQLIdentifierExpr_ptr(new SQLIdentifierExpr(make_string_ptr("CURRENT ROW")));
          }
          else if (lexer->identifierEquals(FnvHash::Constants::UNBOUNDED))
          {
            lexer->nextToken();
            if (encode_util::UnicodeToUTF8(*lexer->stringVal()) == ("PRECEDING"))
            {
              lexer->nextToken();
              beginExpr = SQLIdentifierExpr_ptr(new SQLIdentifierExpr(make_string_ptr("UNBOUNDED PRECEDING")));
            }
            else
            {
              std::string tmp = "syntax error. ";
              tmp += encode_util::UnicodeToUTF8(lexer->info());
              throw new ParserException(make_string_ptr(tmp));
            }
          }
          else
          {
            beginExpr = relational();
          }

          SQLOver_WindowingBound_ptr beginBound = SQLExprParser::parseWindowingBound();
          if (beginBound != nullptr)
          {
            over->setWindowingBetweenBeginBound(beginBound);
          }

          accept(Token::AND);
          SQLExpr_ptr endExpr;
          if (lexer->identifierEquals(FnvHash::Constants::CURRENT))
          {
            lexer->nextToken();
            accept(Token::ROW);
            endExpr = SQLIdentifierExpr_ptr(new SQLIdentifierExpr(make_string_ptr("CURRENT ROW")));
          }
          else if (lexer->identifierEquals(FnvHash::Constants::UNBOUNDED))
          {
            lexer->nextToken();
            if (encode_util::UnicodeToUTF8(*lexer->stringVal()) == ("PRECEDING"))
            {
              lexer->nextToken();
              endExpr = SQLIdentifierExpr_ptr(new SQLIdentifierExpr(make_string_ptr("UNBOUNDED PRECEDING")));
            }
            else
            {
              std::string tmp = "syntax error. ";
              tmp += encode_util::UnicodeToUTF8(lexer->info());
              throw new ParserException(make_string_ptr(tmp));
            }
          }
          else
          {
            endExpr = relational();
          }

          SQLExpr_ptr expr = SQLBetweenExpr_ptr(new SQLBetweenExpr(nullptr, beginExpr, endExpr));
          windowing->setExpr(expr);
        }
        else
        {
          SQLExpr_ptr expr = this->expr();
          windowing->setExpr(expr);

          acceptIdentifier("PRECEDING");
          over->setWindowingPreceding(BOOL::TRUE);
        }

        over->setWindowing(windowing);
      }
    }

    accept(Token::RPAREN);

    aggregateExpr->setOver(over);
  }
  return aggregateExpr;
}

// @SuppressWarnings("unused")
OracleIntervalType_ptr OracleExprParser::parseIntervalType()
{
  string_ptr currentTokenUpperValue = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
  lexer->nextToken();

  if (currentTokenUpperValue->c_str() == ("YEAR"))
  {
    return OracleIntervalType::YEAR;
  }
  if (currentTokenUpperValue->c_str() == ("MONTH"))
  {
    return OracleIntervalType::MONTH;
  }
  if (currentTokenUpperValue->c_str() == ("HOUR"))
  {
    return OracleIntervalType::HOUR;
  }
  if (currentTokenUpperValue->c_str() == ("MINUTE"))
  {
    return OracleIntervalType::MINUTE;
  }
  if (currentTokenUpperValue->c_str() == ("SECOND"))
  {
    return OracleIntervalType::SECOND;
  }
  std::string tmp = "syntax error. ";
  tmp += encode_util::UnicodeToUTF8(lexer->info());
  throw new ParserException(make_string_ptr(tmp));
}

// @Override
SQLSelectParser_ptr OracleExprParser::createSelectParser()
{
  return OracleSelectParser_ptr(new OracleSelectParser(OracleExprParser_ptr(this)));
}

SQLExpr_ptr OracleExprParser::parseInterval()
{
  accept(Token::INTERVAL);

  OracleIntervalExpr_ptr interval = OracleIntervalExpr_ptr(new OracleIntervalExpr());

  if (lexer->token()->name->c_str() == Token::LITERAL_CHARS.name->c_str())
  {
    interval->setValue(SQLCharExpr_ptr(new SQLCharExpr(make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal())))));
  }
  else if (lexer->token()->name->c_str() == Token::VARIANT.name->c_str())
  {
    interval->setValue(SQLVariantRefExpr_ptr(new SQLVariantRefExpr(make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal())))));
  }
  else if (lexer->token()->name->c_str() == Token::QUES.name->c_str())
  {
    interval->setValue(SQLVariantRefExpr_ptr(new SQLVariantRefExpr("?")));
  }
  else
  {
    return SQLIdentifierExpr_ptr(new SQLIdentifierExpr(make_string_ptr("INTERVAL")));
  }

  lexer->nextToken();

  OracleIntervalType_ptr type;
  if (lexer->identifierEquals(FnvHash::Constants::YEAR))
  {
    lexer->nextToken();
    type = OracleIntervalType::YEAR;
  }
  else if (lexer->identifierEquals(FnvHash::Constants::MONTH))
  {
    lexer->nextToken();
    type = OracleIntervalType::MONTH;
  }
  else if (lexer->identifierEquals(FnvHash::Constants::DAY))
  {
    lexer->nextToken();
    type = OracleIntervalType::DAY;
  }
  else if (lexer->identifierEquals(FnvHash::Constants::HOUR))
  {
    lexer->nextToken();
    type = OracleIntervalType::HOUR;
  }
  else if (lexer->identifierEquals(FnvHash::Constants::MINUTE))
  {
    lexer->nextToken();
    type = OracleIntervalType::MINUTE;
  }
  else if (lexer->identifierEquals(FnvHash::Constants::SECOND))
  {
    lexer->nextToken();
    type = OracleIntervalType::SECOND;
  }
  else
  {
    std::string tmp = "illegal interval type. ";
    tmp += encode_util::UnicodeToUTF8(lexer->info());
    throw new ParserException(make_string_ptr(tmp));
  }

  interval->setType(type);

  if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
  {
    lexer->nextToken();
    if (lexer->token()->name->c_str() != Token::LITERAL_INT.name->c_str() &&
        lexer->token()->name->c_str() != Token::VARIANT.name->c_str())
    {
      std::string tmp = "syntax error. ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }
    interval->setPrecision(this->primary());

    if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
    {
      lexer->nextToken();
      if (lexer->token()->name->c_str() != Token::LITERAL_INT.name->c_str())
      {
        std::string tmp = "syntax error. ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }
      interval->setFactionalSecondsPrecision(lexer->integerValue());
      lexer->nextToken();
    }
    accept(Token::RPAREN);
  }

  if (lexer->token()->name->c_str() == Token::TO.name->c_str())
  {
    lexer->nextToken();
    if (lexer->identifierEquals(make_string_ptr("SECOND")))
    {
      lexer->nextToken();
      interval->setToType(OracleIntervalType::SECOND);
      if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
      {
        lexer->nextToken();
        if (lexer->token()->name->c_str() != Token::LITERAL_INT.name->c_str() &&
            lexer->token()->name->c_str() != Token::VARIANT.name->c_str())
        {
          std::string tmp = "syntax error. ";
          tmp += encode_util::UnicodeToUTF8(lexer->info());
          throw new ParserException(make_string_ptr(tmp));
        }
        interval->setToFactionalSecondsPrecision(primary());
        accept(Token::RPAREN);
      }
    }
    else
    {
      interval->setToType(OracleIntervalType::MONTH);
      lexer->nextToken();
    }
  }

  return interval;
}

SQLExpr_ptr OracleExprParser::relationalRest(SQLExpr_ptr expr)
{
  if (lexer->token()->name->c_str() == Token::IS.name->c_str())
  {
    lexer->nextToken();

    if (lexer->token()->name->c_str() == Token::NOT.name->c_str())
    {
      lexer->nextToken();
      SQLExpr_ptr rightExpr = primary();
      expr = SQLBinaryOpExpr_ptr(new SQLBinaryOpExpr(expr, SQLBinaryOperator_ptr(new SQLBinaryOperator(&SQLBinaryOperator::IsNot)), rightExpr, getDbType()));
    }
    else if (lexer->identifierEquals(make_string_ptr("A")))
    {
      lexer->nextToken();
      accept(Token::SET);
      expr = OracleIsSetExpr_ptr(new OracleIsSetExpr(expr));
    }
    else if (lexer->token()->name->c_str() == Token::OF.name->c_str())
    {
      lexer->nextToken();

      if (lexer->identifierEquals(FnvHash::Constants::TYPE))
      {
        lexer->nextToken();
      }

      OracleIsOfTypeExpr_ptr isOf = OracleIsOfTypeExpr_ptr(new OracleIsOfTypeExpr());
      isOf->setExpr(expr);
      accept(Token::LPAREN);

      for (;;)
      {
        bool only = lexer->identifierEquals(FnvHash::Constants::ONLY);
        if (only)
        {
          lexer->nextToken();
        }

        SQLExpr_ptr type = this->name();
        if (only)
        {
          Object_ptr value = Object_ptr(new Object());
          value->any_data_ = BOOL::TRUE;
          type->putAttribute( "ONLY", value);
        }

        type->setParent(isOf);
        isOf->getTypes()->push_back(type);

        if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
        {
          lexer->nextToken();
          continue;
        }
        break;
      }

      accept(Token::RPAREN);

      expr = isOf;
    }
    else
    {
      SQLExpr_ptr rightExpr = primary();
      expr = SQLBinaryOpExpr_ptr(new SQLBinaryOpExpr(expr, SQLBinaryOperator_ptr(new SQLBinaryOperator(&SQLBinaryOperator::Is)), rightExpr, getDbType()));
    }

    return expr;
  }
  return SQLExprParser::relationalRest(expr);
}

SQLName_ptr OracleExprParser::name()
{
  SQLName_ptr name = SQLExprParser::name();

  if (lexer->token()->name->c_str() == Token::MONKEYS_AT.name->c_str())
  {
    lexer->nextToken();
    if (lexer->token()->name->c_str() != Token::IDENTIFIER.name->c_str())
    {
      std::string tmp = "syntax error, expect identifier, but ";
      tmp += lexer->token()->name->c_str();
      tmp += ", ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }
    SQLDbLinkExpr_ptr dbLink = SQLDbLinkExpr_ptr(new SQLDbLinkExpr());
    dbLink->setExpr(name);

    string_ptr link = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
    lexer->nextToken();
    while (lexer->token()->name->c_str() == Token::DOT.name->c_str())
    {
      lexer->nextToken();

      string_ptr stringVal_ = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      accept(Token::IDENTIFIER);
      link->append(".");
      link->append(stringVal_->c_str());
    }

    dbLink->setDbLink(link);
    return dbLink;
  }
  //
  //        if (name.nameHashCode64() == FnvHash::Constants::UNSUPPORTED
  //                && lexer->identifierEquals(FnvHash::Constants::TYPE)) {
  //            name = new SQLIdentifierExpr(name.getSimpleName() + " " + make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal())));
  //            lexer->nextToken();
  //        }

  return name;
}

OraclePrimaryKey_ptr OracleExprParser::parsePrimaryKey()
{
  lexer->nextToken();
  accept(Token::KEY);

  OraclePrimaryKey_ptr primaryKey = OraclePrimaryKey_ptr(new OraclePrimaryKey());
  accept(Token::LPAREN);
  orderBy(primaryKey->getColumns(), primaryKey);
  accept(Token::RPAREN);

  if (lexer->token()->name->c_str() == Token::USING.name->c_str())
  {
    OracleUsingIndexClause_ptr using_ = parseUsingIndex();
    primaryKey->setUsing(using_);
  }

  for (;;)
  {
    if (lexer->token()->name->c_str() == Token::ENABLE.name->c_str())
    {
      lexer->nextToken();
      primaryKey->setEnable(BOOL::TRUE);
    }
    else if (lexer->token()->name->c_str() == Token::DISABLE.name->c_str())
    {
      lexer->nextToken();
      primaryKey->setEnable(BOOL::FALSE);
    }
    else if (lexer->identifierEquals(make_string_ptr("VALIDATE")))
    {
      lexer->nextToken();
      primaryKey->SQLConstraintImpl::setValidate(BOOL::TRUE);
    }
    else if (lexer->identifierEquals(make_string_ptr("NOVALIDATE")))
    {
      lexer->nextToken();
      primaryKey->SQLConstraintImpl::setValidate(BOOL::FALSE);
    }
    else if (lexer->identifierEquals(make_string_ptr("RELY")))
    {
      lexer->nextToken();
      primaryKey->setRely(BOOL::TRUE);
    }
    else if (lexer->identifierEquals(make_string_ptr("NORELY")))
    {
      lexer->nextToken();
      primaryKey->setRely(BOOL::FALSE);
    }
    else
    {
      break;
    }
  }

  return primaryKey;
}

OracleUsingIndexClause_ptr OracleExprParser::parseUsingIndex()
{
  accept(Token::USING);
  accept(Token::INDEX);

  OracleUsingIndexClause_ptr using_ = OracleUsingIndexClause_ptr(new OracleUsingIndexClause());

  if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
  {
    lexer->nextToken();
    OracleCreateIndexStatement_ptr createIndex = std::dynamic_pointer_cast<OracleCreateIndexStatement>((new OracleStatementParser(lexer))->parseCreateIndex(BOOL::TRUE));
    using_->setIndex(createIndex);
    accept(Token::RPAREN);
  }

  for (;;)
  {
    this->parseSegmentAttributes(using_);

    if (lexer->token()->name->c_str() == Token::COMPUTE.name->c_str())
    {
      lexer->nextToken();
      acceptIdentifier("STATISTICS");
      using_->setComputeStatistics(BOOL::TRUE);
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::ENABLE.name->c_str())
    {
      lexer->nextToken();
      using_->setEnable(BOOL::TRUE);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("REVERSE")))
    {
      lexer->nextToken();
      using_->setReverse(BOOL::TRUE);
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::DISABLE.name->c_str())
    {
      lexer->nextToken();
      using_->setEnable(BOOL::FALSE);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("LOCAL")))
    {
      lexer->nextToken();
      accept(Token::LPAREN);

      // http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_5010.htm#i2125897
      for (;;)
      {
        SQLPartition_ptr partition = this->parsePartition();
        partition->setParent(using_);
        using_->getLocalPartitionIndex()->push_back(partition);

        if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
        {
          lexer->nextToken();
          continue;
        }
        else if (lexer->token()->name->c_str() == Token::RPAREN.name->c_str())
        {
          break;
        }
        else
        {
          std::string tmp = "TODO ";
          tmp += encode_util::UnicodeToUTF8(lexer->info());
          throw new ParserException(make_string_ptr(tmp));
        }
      }
      accept(Token::RPAREN);
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str())
    {
      using_->setTablespace(this->name());
      break;
    }
    else
    {
      break;
    }
  }

  return using_;
}

SQLColumnDefinition_ptr OracleExprParser::parseColumnRest(SQLColumnDefinition_ptr column)
{
  column = SQLExprParser::parseColumnRest(column);

  if (lexer->identifierEquals(FnvHash::Constants::GENERATED))
  {
    lexer->nextToken();
    if (lexer->identifierEquals(FnvHash::Constants::ALWAYS))
    {
      lexer->nextToken();
    }
    else
    {
      std::string tmp = "TODO ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }

    accept(Token::AS);
    SQLExpr_ptr expr = this->expr();

    column->setGeneratedAlawsAs(expr);
  }

  for (;;)
  {
    if (lexer->token()->name->c_str() == Token::ENABLE.name->c_str())
    {
      lexer->nextToken();
      column->setEnable(BOOL::TRUE);
    }
    else if (lexer->token()->name->c_str() == Token::DISABLE.name->c_str())
    {
      lexer->nextToken();
      column->setEnable(BOOL::FALSE);
    }
    else if (lexer->identifierEquals(make_string_ptr("VALIDATE")))
    {
      lexer->nextToken();
      column->setValidate(BOOL::TRUE);
    }
    else if (lexer->identifierEquals(make_string_ptr("NOVALIDATE")))
    {
      lexer->nextToken();
      column->setValidate(BOOL::FALSE);
    }
    else if (lexer->identifierEquals(make_string_ptr("RELY")))
    {
      lexer->nextToken();
      column->setRely(BOOL::TRUE);
    }
    else if (lexer->identifierEquals(make_string_ptr("NORELY")))
    {
      lexer->nextToken();
      column->setRely(BOOL::FALSE);
    }
    else if (lexer->identifierEquals(FnvHash::Constants::VISIBLE))
    {
      lexer->nextToken();
      column->setVisible(BOOL::TRUE);
    }
    else if (lexer->identifierEquals(FnvHash::Constants::VIRTUAL))
    {
      lexer->nextToken();
      column->setVirtual(BOOL::TRUE);
    }
    else
    {
      break;
    }
  }

  return column;
}

SQLExpr_ptr OracleExprParser::exprRest(SQLExpr_ptr expr_)
{
  expr_ = SQLExprParser::exprRest(expr_);

  if (lexer->token()->name->c_str() == Token::COLONEQ.name->c_str())
  {
    lexer->nextToken();
    SQLExpr_ptr right = getExpr();
    expr_ = SQLBinaryOpExpr_ptr(new SQLBinaryOpExpr(expr_, SQLBinaryOperator_ptr(new SQLBinaryOperator(&SQLBinaryOperator::Assignment)), right, getDbType()));
  }

  return expr_;
}

SQLObject_ptr OracleExprParser::parseOpaque()
{
  acceptIdentifier("OPAQUE");
  acceptIdentifier("TYPE");

  SQLExpr_ptr expr = this->primary();

  OracleLobStorageClause_ptr clause = OracleLobStorageClause_ptr(new OracleLobStorageClause());

  accept(Token::STORE);
  accept(Token::AS);

  if (lexer->identifierEquals(make_string_ptr("SECUREFILE")))
  {
    lexer->nextToken();
    clause->setSecureFile(BOOL::TRUE);
  }

  if (lexer->identifierEquals(make_string_ptr("BASICFILE")))
  {
    lexer->nextToken();
    clause->setBasicFile(BOOL::TRUE);
  }

  accept(Token::LOB);

  if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
  {
    lexer->nextToken();

    for (;;)
    {
      this->parseSegmentAttributes(clause);

      if (lexer->token()->name->c_str() == Token::ENABLE.name->c_str())
      {
        lexer->nextToken();
        accept(Token::STORAGE);
        accept(Token::IN);
        accept(Token::ROW);
        clause->setEnable(BOOL::TRUE);
        continue;
      }
      else if (lexer->token()->name->c_str() == Token::DISABLE.name->c_str())
      {
        lexer->nextToken();
        accept(Token::STORAGE);
        accept(Token::IN);
        accept(Token::ROW);
        clause->setEnable(BOOL::FALSE);
        continue;
      }

      if (lexer->token()->name->c_str() == Token::CHUNK.name->c_str())
      {
        lexer->nextToken();
        clause->setChunk(this->primary());
        continue;
      }

      if (lexer->token()->name->c_str() == Token::NOCACHE.name->c_str())
      {
        lexer->nextToken();
        clause->setCache(BOOL::FALSE);
        if (lexer->token()->name->c_str() == Token::LOGGING.name->c_str())
        {
          lexer->nextToken();
          clause->setLogging(BOOL::TRUE);
        }
        continue;
      }

      if (lexer->token()->name->c_str() == Token::CACHE.name->c_str())
      {
        lexer->nextToken();
        clause->setCache(BOOL::TRUE);
        continue;
      }

      if (lexer->token()->name->c_str() == Token::KEEP_DUPLICATES.name->c_str())
      {
        lexer->nextToken();
        clause->setKeepDuplicate(BOOL::TRUE);
        continue;
      }

      if (lexer->identifierEquals(make_string_ptr("PCTVERSION")))
      {
        lexer->nextToken();
        clause->setPctversion(this->expr());
        continue;
      }

      if (lexer->identifierEquals(make_string_ptr("RETENTION")))
      {
        lexer->nextToken();
        clause->setRetention(BOOL::TRUE);
        continue;
      }

      if (lexer->token()->name->c_str() == Token::STORAGE.name->c_str())
      {
        OracleStorageClause_ptr storageClause = this->parseStorage();
        clause->setStorageClause(storageClause);
        continue;
      }

      break;
    }

    accept(Token::RPAREN);
  }

  return clause;
}

OracleLobStorageClause_ptr OracleExprParser::parseLobStorage()
{
  lexer->nextToken();

  OracleLobStorageClause_ptr clause = OracleLobStorageClause_ptr(new OracleLobStorageClause());

  accept(Token::LPAREN);
  this->names(clause->getItems());
  accept(Token::RPAREN);

  accept(Token::STORE);
  accept(Token::AS);

  if (lexer->identifierEquals(make_string_ptr("SECUREFILE")))
  {
    lexer->nextToken();
    clause->setSecureFile(BOOL::TRUE);
  }

  if (lexer->identifierEquals(make_string_ptr("BASICFILE")))
  {
    lexer->nextToken();
    clause->setBasicFile(BOOL::TRUE);
  }

  if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str() || lexer->token()->name->c_str() == Token::LITERAL_ALIAS.name->c_str())
  {
    SQLName_ptr segmentName = this->name();
    clause->setSegementName(segmentName);
  }

  if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
  {
    lexer->nextToken();

    for (;;)
    {
      this->parseSegmentAttributes(clause);

      if (lexer->token()->name->c_str() == Token::ENABLE.name->c_str())
      {
        lexer->nextToken();
        accept(Token::STORAGE);
        accept(Token::IN);
        accept(Token::ROW);
        clause->setEnable(BOOL::TRUE);
        continue;
      }
      else if (lexer->token()->name->c_str() == Token::DISABLE.name->c_str())
      {
        lexer->nextToken();
        accept(Token::STORAGE);
        accept(Token::IN);
        accept(Token::ROW);
        clause->setEnable(BOOL::FALSE);
        continue;
      }

      if (lexer->token()->name->c_str() == Token::CHUNK.name->c_str())
      {
        lexer->nextToken();
        clause->setChunk(this->primary());
        continue;
      }

      if (lexer->token()->name->c_str() == Token::NOCACHE.name->c_str())
      {
        lexer->nextToken();
        clause->setCache(BOOL::FALSE);
        if (lexer->token()->name->c_str() == Token::LOGGING.name->c_str())
        {
          lexer->nextToken();
          clause->setLogging(BOOL::TRUE);
        }
        continue;
      }

      if (lexer->token()->name->c_str() == Token::CACHE.name->c_str())
      {
        lexer->nextToken();
        clause->setCache(BOOL::TRUE);
        continue;
      }

      if (lexer->token()->name->c_str() == Token::KEEP_DUPLICATES.name->c_str())
      {
        lexer->nextToken();
        clause->setKeepDuplicate(BOOL::TRUE);
        continue;
      }

      if (lexer->identifierEquals(make_string_ptr("PCTVERSION")))
      {
        lexer->nextToken();
        clause->setPctversion(this->expr());
        continue;
      }

      if (lexer->identifierEquals(make_string_ptr("RETENTION")))
      {
        lexer->nextToken();
        clause->setRetention(BOOL::TRUE);
        continue;
      }

      if (lexer->token()->name->c_str() == Token::STORAGE.name->c_str())
      {
        OracleStorageClause_ptr storageClause = this->parseStorage();
        clause->setStorageClause(storageClause);
        continue;
      }

      break;
    }

    accept(Token::RPAREN);
  }

  return clause;
}

OracleStorageClause_ptr OracleExprParser::parseStorage()
{
  lexer->nextToken();
  accept(Token::LPAREN);

  OracleStorageClause_ptr storage = OracleStorageClause_ptr(new OracleStorageClause());
  for (;;)
  {
    if (lexer->identifierEquals(make_string_ptr("INITIAL")))
    {
      lexer->nextToken();
      storage->setInitial(this->expr());
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::NEXT.name->c_str())
    {
      lexer->nextToken();
      storage->setNext(this->expr());
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::MINEXTENTS.name->c_str())
    {
      lexer->nextToken();
      storage->setMinExtents(this->expr());
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::MAXEXTENTS.name->c_str())
    {
      lexer->nextToken();
      storage->setMaxExtents(this->expr());
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::MAXSIZE.name->c_str())
    {
      lexer->nextToken();
      storage->setMaxSize(this->expr());
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::PCTINCREASE.name->c_str())
    {
      lexer->nextToken();
      storage->setPctIncrease(this->expr());
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("FREELISTS")))
    {
      lexer->nextToken();
      storage->setFreeLists(this->expr());
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("FREELIST")))
    {
      lexer->nextToken();
      acceptIdentifier("GROUPS");
      storage->setFreeListGroups(this->expr());
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("BUFFER_POOL")))
    {
      lexer->nextToken();
      storage->setBufferPool(this->expr());
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("OBJNO")))
    {
      lexer->nextToken();
      storage->setObjno(this->expr());
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::FLASH_CACHE.name->c_str())
    {
      lexer->nextToken();
      OracleStorageClause_FlashCacheType_ptr flashCacheType;
      if (lexer->identifierEquals(make_string_ptr("KEEP")))
      {
        flashCacheType = OracleStorageClause_FlashCacheType::KEEP;
        lexer->nextToken();
      }
      else if (lexer->token()->name->c_str() == Token::NONE.name->c_str())
      {
        flashCacheType = OracleStorageClause_FlashCacheType::NONE;
        lexer->nextToken();
      }
      else
      {
        accept(Token::DEFAULT);
        flashCacheType = OracleStorageClause_FlashCacheType::DEFAULT;
      }
      storage->setFlashCache(flashCacheType);
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::CELL_FLASH_CACHE.name->c_str())
    {
      lexer->nextToken();
      OracleStorageClause_FlashCacheType_ptr flashCacheType;
      if (lexer->identifierEquals(make_string_ptr("KEEP")))
      {
        flashCacheType = OracleStorageClause_FlashCacheType::KEEP;
        lexer->nextToken();
      }
      else if (lexer->token()->name->c_str() == Token::NONE.name->c_str())
      {
        flashCacheType = OracleStorageClause_FlashCacheType::NONE;
        lexer->nextToken();
      }
      else
      {
        accept(Token::DEFAULT);
        flashCacheType = OracleStorageClause_FlashCacheType::DEFAULT;
      }
      storage->setCellFlashCache(flashCacheType);
      continue;
    }

    break;
  }
  accept(Token::RPAREN);
  return storage;
}

SQLUnique_ptr OracleExprParser::parseUnique()
{
  accept(Token::UNIQUE);

  OracleUnique_ptr unique = OracleUnique_ptr(new OracleUnique());
  accept(Token::LPAREN);
  orderBy(unique->getColumns(), unique);
  accept(Token::RPAREN);

  if (lexer->token()->name->c_str() == Token::USING.name->c_str())
  {
    OracleUsingIndexClause_ptr using_ = parseUsingIndex();
    unique->setUsing(using_);
  }

  return unique;
}

OracleConstraint_ptr OracleExprParser::parseConstaint()
{
  OracleConstraint_ptr constraint = std::dynamic_pointer_cast<OracleConstraint>(SQLExprParser::parseConstaint());

  for (;;)
  {
    if (lexer->token()->name->c_str() == Token::EXCEPTIONS.name->c_str())
    {
      lexer->nextToken();
      accept(Token::INTO);
      SQLName_ptr exceptionsInto = this->name();
      constraint->setExceptionsInto(exceptionsInto);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::DISABLE.name->c_str())
    {
      lexer->nextToken();
      constraint->setEnable(BOOL::FALSE);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::ENABLE.name->c_str())
    {
      lexer->nextToken();
      constraint->setEnable(BOOL::TRUE);
      continue;
    }

    if (lexer->identifierEquals(FnvHash::Constants::VALIDATE))
    {
      lexer->nextToken();
      constraint->setValidate(BOOL::TRUE);
      continue;
    }
    if (lexer->identifierEquals(FnvHash::Constants::NOVALIDATE))
    {
      lexer->nextToken();
      constraint->setValidate(BOOL::FALSE);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::INITIALLY.name->c_str())
    {
      lexer->nextToken();

      if (lexer->token()->name->c_str() == Token::IMMEDIATE.name->c_str())
      {
        lexer->nextToken();
        constraint->setInitially(OracleConstraint_Initially::IMMEDIATE);
      }
      else
      {
        accept(Token::DEFERRED);
        constraint->setInitially(OracleConstraint_Initially::DEFERRED);
      }

      continue;
    }

    if (lexer->token()->name->c_str() == Token::NOT.name->c_str())
    {
      lexer->nextToken();
      if (lexer->identifierEquals(FnvHash::Constants::DEFERRABLE))
      {
        lexer->nextToken();
        constraint->setDeferrable(BOOL::FALSE);
        continue;
      }
      std::string tmp = "TODO ";
      tmp += encode_util::UnicodeToUTF8(lexer->info());
      throw new ParserException(make_string_ptr(tmp));
    }

    if (lexer->identifierEquals(FnvHash::Constants::DEFERRABLE))
    {
      lexer->nextToken();
      constraint->setDeferrable(BOOL::TRUE);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::USING.name->c_str())
    {
      OracleUsingIndexClause_ptr using_ = parseUsingIndex();
      constraint->setUsing(using_);
    }

    break;
  }

  return constraint;
}

OracleForeignKey_ptr OracleExprParser::createForeignKey()
{
  return OracleForeignKey_ptr(new OracleForeignKey());
}

SQLCheck_ptr OracleExprParser::createCheck()
{
  return OracleCheck_ptr(new OracleCheck());
}

SQLPartition_ptr OracleExprParser::parsePartition()
{
  accept(Token::PARTITION);
  SQLPartition_ptr partition = SQLPartition_ptr(new SQLPartition());
  partition->setName(this->name());

  SQLPartitionValue_ptr values = this->parsePartitionValues();
  if (values != nullptr)
  {
    this->parseSegmentAttributes(values);
  }

  if (values != nullptr)
  {
    partition->setValues(values);
  }

  if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
  {
    lexer->nextToken();

    for (;;)
    {
      SQLSubPartition_ptr subPartition = parseSubPartition();
      this->parseSegmentAttributes(subPartition);

      partition->addSubPartition(subPartition);

      if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
      {
        lexer->nextToken();
        continue;
      }

      break;
    }

    accept(Token::RPAREN);
  }
  else if (lexer->identifierEquals(FnvHash::Constants::SUBPARTITIONS))
  {
    lexer->nextToken();
    SQLExpr_ptr subPartitionsCount = this->primary();
    partition->setSubPartitionsCount(subPartitionsCount);
  }

  for (;;)
  {
    parseSegmentAttributes(partition);

    if (lexer->token()->name->c_str() == Token::LOB.name->c_str())
    {
      OracleLobStorageClause_ptr lobStorage = this->parseLobStorage();
      partition->setLobStorage(lobStorage);
      continue;
    }

    if (lexer->token()->name->c_str() == Token::SEGMENT.name->c_str() ||
        lexer->identifierEquals(make_string_ptr("SEGMENT")))
    {
      lexer->nextToken();
      accept(Token::CREATION);
      if (lexer->token()->name->c_str() == Token::IMMEDIATE.name->c_str())
      {
        lexer->nextToken();
        partition->setSegmentCreationImmediate(BOOL::TRUE);
      }
      else if (lexer->token()->name->c_str() == Token::DEFERRED.name->c_str())
      {
        lexer->nextToken();
        partition->setSegmentCreationDeferred(BOOL::TRUE);
      }
      continue;
    }
    break;
  }

  if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
  {
    lexer->nextToken();

    for (;;)
    {
      SQLSubPartition_ptr subPartition = parseSubPartition();
      this->parseSegmentAttributes(subPartition);

      partition->addSubPartition(subPartition);

      if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
      {
        lexer->nextToken();
        continue;
      }

      break;
    }

    accept(Token::RPAREN);
  }

  return partition;
}

SQLPartitionBy_ptr OracleExprParser::parsePartitionBy()
{
  lexer->nextToken();

  accept(Token::BY);

  SQLPartitionBy_ptr partitionBy;

  if (lexer->identifierEquals(make_string_ptr("RANGE")))
  {
    return this->partitionByRange();
  }
  else if (lexer->identifierEquals(make_string_ptr("HASH")))
  {
    SQLPartitionByHash_ptr partitionByHash = this->partitionByHash();
    this->partitionClauseRest(partitionByHash);

    if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      lexer->nextToken();
      for (;;)
      {
        SQLPartition_ptr partition = this->parsePartition();
        partitionByHash->addPartition(partition);
        if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
        {
          lexer->nextToken();
          continue;
        }
        else if (lexer->token()->name->c_str() == Token::RPAREN.name->c_str())
        {
          lexer->nextToken();
          break;
        }
        std::string tmp = "TODO : ";
        tmp += encode_util::UnicodeToUTF8(lexer->info());
        throw new ParserException(make_string_ptr(tmp));
      }
    }
    return partitionByHash;
  }
  else if (lexer->identifierEquals(make_string_ptr("LIST")))
  {
    SQLPartitionByList_ptr partitionByList_ = partitionByList();
    this->partitionClauseRest(partitionByList_);
    return partitionByList_;
  }
  else
  {
    std::string tmp = "TODO : ";
    tmp += encode_util::UnicodeToUTF8(lexer->info());
    throw new ParserException(make_string_ptr(tmp));
  }
}

SQLPartitionByList_ptr OracleExprParser::partitionByList()
{
  acceptIdentifier("LIST");
  SQLPartitionByList_ptr partitionByList = SQLPartitionByList_ptr(new SQLPartitionByList());

  accept(Token::LPAREN);
  partitionByList->addColumn(this->expr());
  accept(Token::RPAREN);

  this->parsePartitionByRest(partitionByList);

  return partitionByList;
}

SQLSubPartition_ptr OracleExprParser::parseSubPartition()
{
  acceptIdentifier("SUBPARTITION");

  SQLSubPartition_ptr subPartition = SQLSubPartition_ptr(new SQLSubPartition());
  SQLName_ptr name = this->name();
  subPartition->setName(name);

  SQLPartitionValue_ptr values = this->parsePartitionValues();
  if (values != nullptr)
  {
    subPartition->setValues(values);
  }

  if (lexer->token()->name->c_str() == Token::TABLESPACE.name->c_str())
  {
    lexer->nextToken();
    subPartition->setTableSpace(this->name());
  }

  return subPartition;
}

void OracleExprParser::parseSegmentAttributes(OracleSegmentAttributes_ptr attributes)
{
  for (;;)
  {
    if (lexer->token()->name->c_str() == Token::TABLESPACE.name->c_str())
    {
      lexer->nextToken();
      attributes->setTablespace(this->name());
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::NOCOMPRESS.name->c_str() ||
             lexer->identifierEquals(make_string_ptr("NOCOMPRESS")))
    {
      lexer->nextToken();
      attributes->setCompress(BOOL::FALSE);
      continue;
    }
    else if (lexer->identifierEquals(FnvHash::Constants::COMPRESS))
    {
      lexer->nextToken();
      attributes->setCompress(BOOL::TRUE);

      if (lexer->token()->name->c_str() == Token::LITERAL_INT.name->c_str())
      {
        int compressLevel = this->parseIntValue();
        attributes->setCompressLevel(compressLevel);
      }
      else if (lexer->identifierEquals(make_string_ptr("BASIC")))
      {
        lexer->nextToken();
        // TODO COMPRESS BASIC
      }
      else if (lexer->token()->name->c_str() == Token::FOR.name->c_str())
      {
        lexer->nextToken();
        if (lexer->identifierEquals(make_string_ptr("OLTP")))
        {
          lexer->nextToken();
          attributes->setCompressForOltp(BOOL::TRUE);
        }
        else
        {
          std::string tmp = "TODO : ";
          tmp += encode_util::UnicodeToUTF8(lexer->info());
          throw new ParserException(make_string_ptr(tmp));
        }
      }
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("NOCOMPRESS")))
    {
      lexer->nextToken();
      attributes->setCompress(BOOL::FALSE);
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::LOGGING.name->c_str() || lexer->identifierEquals(make_string_ptr("LOGGING")))
    {
      lexer->nextToken();
      attributes->setLogging(BOOL::TRUE);
      continue;
    }
    else if (lexer->identifierEquals(make_string_ptr("NOLOGGING")))
    {
      lexer->nextToken();
      attributes->setLogging(BOOL::FALSE);
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::INITRANS.name->c_str())
    {
      lexer->nextToken();
      attributes->setInitrans(this->parseIntValue());
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::MAXTRANS.name->c_str())
    {
      lexer->nextToken();
      attributes->setMaxtrans(this->parseIntValue());
    }
    else if (lexer->token()->name->c_str() == Token::PCTINCREASE.name->c_str())
    {
      lexer->nextToken();
      attributes->setPctincrease(this->parseIntValue());
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::PCTFREE.name->c_str())
    {
      lexer->nextToken();
      attributes->setPctfree(this->parseIntValue());
      continue;
    }
    else if (lexer->token()->name->c_str() == Token::STORAGE.name->c_str() ||
             lexer->identifierEquals(make_string_ptr("STORAGE")))
    {
      OracleStorageClause_ptr storage = this->parseStorage();
      attributes->setStorage(storage);
      continue;
    }
    else if (lexer->identifierEquals(FnvHash::Constants::PCTUSED))
    {
      lexer->nextToken();
      attributes->setPctused(this->parseIntValue());
      continue;
    }
    else if (lexer->identifierEquals(FnvHash::Constants::USAGE))
    {
      lexer->nextToken();
      acceptIdentifier("QUEUE");
      // TODO USAGE QUEUE
      continue;
    }
    else if (lexer->identifierEquals(FnvHash::Constants::OPAQUE))
    {
      parseOpaque();
      // TODO OPAQUE TYPE
      continue;
    }
    else
    {
      break;
    }
  }
}

SQLPartitionByRange_ptr OracleExprParser::partitionByRange()
{
  acceptIdentifier("RANGE");
  accept(Token::LPAREN);
  SQLPartitionByRange_ptr clause = SQLPartitionByRange_ptr(new SQLPartitionByRange());
  for (;;)
  {
    SQLName_ptr column = this->name();
    clause->addColumn(column);

    if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
    {
      lexer->nextToken();
      continue;
    }

    break;
  }
  accept(Token::RPAREN);

  if (lexer->token()->name->c_str() == Token::INTERVAL.name->c_str())
  {
    lexer->nextToken();
    accept(Token::LPAREN);
    clause->setInterval(this->expr());
    accept(Token::RPAREN);
  }

  parsePartitionByRest(clause);

  return clause;
}

void OracleExprParser::parsePartitionByRest(SQLPartitionBy_ptr clause)
{
  if (lexer->token()->name->c_str() == Token::STORE.name->c_str())
  {
    lexer->nextToken();
    accept(Token::IN);
    accept(Token::LPAREN);
    for (;;)
    {
      SQLName_ptr tablespace = this->name();
      clause->getStoreIn()->push_back(tablespace);

      if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
      {
        lexer->nextToken();
        continue;
      }

      break;
    }
    accept(Token::RPAREN);
  }

  if (lexer->identifierEquals(FnvHash::Constants::SUBPARTITION))
  {
    SQLSubPartitionBy_ptr subPartitionBy_ = subPartitionBy();
    clause->setSubPartitionBy(subPartitionBy_);

    if (lexer->identifierEquals(FnvHash::Constants::SUBPARTITIONS))
    {
      lexer->nextToken();
      long intValue = lexer->integerValue();
      SQLNumberExpr_ptr numExpr = SQLNumberExpr_ptr(new SQLNumberExpr(intValue));
      subPartitionBy_->setSubPartitionsCount(numExpr);
      lexer->nextToken();
    }
  }

  accept(Token::LPAREN);

  for (;;)
  {
    SQLPartition_ptr partition = this->parsePartition();

    clause->addPartition(partition);

    if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
    {
      lexer->nextToken();
      continue;
    }

    break;
  }

  accept(Token::RPAREN);
}

SQLSubPartitionBy_ptr OracleExprParser::subPartitionBy()
{
  lexer->nextToken();
  accept(Token::BY);

  if (lexer->identifierEquals(FnvHash::Constants::HASH))
  {
    lexer->nextToken();
    accept(Token::LPAREN);

    SQLSubPartitionByHash_ptr byHash = SQLSubPartitionByHash_ptr(new SQLSubPartitionByHash());
    SQLExpr_ptr expr = this->expr();
    byHash->setExpr(expr);
    accept(Token::RPAREN);

    return byHash;
  }
  else if (lexer->identifierEquals(FnvHash::Constants::LIST))
  {
    lexer->nextToken();
    accept(Token::LPAREN);

    SQLSubPartitionByList_ptr byList = SQLSubPartitionByList_ptr(new SQLSubPartitionByList());
    SQLName_ptr column = this->name();
    byList->setColumn(column);
    accept(Token::RPAREN);

    if (lexer->identifierEquals(FnvHash::Constants::SUBPARTITION))
    {
      lexer->nextToken();
      acceptIdentifier("TEMPLATE");
      accept(Token::LPAREN);

      for (;;)
      {
        SQLSubPartition_ptr subPartition = this->parseSubPartition();
        subPartition->setParent(byList);
        byList->getSubPartitionTemplate()->push_back(subPartition);

        if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
        {
          lexer->nextToken();
          continue;
        }
        break;
      }
      accept(Token::RPAREN);
    }

    if (lexer->identifierEquals(FnvHash::Constants::SUBPARTITIONS))
    {
      lexer->nextToken();
      long intValue = lexer->integerValue();
      SQLNumberExpr_ptr numExpr = SQLNumberExpr_ptr(new SQLNumberExpr(intValue));
      byList->setSubPartitionsCount(numExpr);
      lexer->nextToken();
    }

    return byList;
  }

  std::string tmp = "TODO : ";
  tmp += encode_util::UnicodeToUTF8(lexer->info());
  throw new ParserException(make_string_ptr(tmp));
}

void OracleExprParser::partitionClauseRest(SQLPartitionBy_ptr clause)
{
  if (lexer->identifierEquals(FnvHash::Constants::PARTITIONS))
  {
    lexer->nextToken();

    SQLIntegerExpr_ptr countExpr = this->integerExpr();
    clause->setPartitionsCount(countExpr);
  }

  if (lexer->token()->name->c_str() == Token::STORE.name->c_str())
  {
    lexer->nextToken();
    accept(Token::IN);
    accept(Token::LPAREN);
    this->names(clause->getStoreIn(), clause);
    accept(Token::RPAREN);
  }
}

SQLPartitionByHash_ptr OracleExprParser::partitionByHash()
{
  acceptIdentifier("HASH");
  SQLPartitionByHash_ptr partitionByHash = SQLPartitionByHash_ptr(new SQLPartitionByHash());

  if (lexer->token()->name->c_str() == Token::KEY.name->c_str())
  {
    lexer->nextToken();
    partitionByHash->setKey(BOOL::TRUE);
  }

  accept(Token::LPAREN);
  this->exprList(partitionByHash->getColumns(), partitionByHash);
  accept(Token::RPAREN);
  return partitionByHash;
}
